<html>
<head>
  <script src="bower_components/jquery/dist/jquery.js"></script>
  <script src="bower_components/jquery-ui/ui/jquery-ui.js"></script>
  <script src="bower_components/tv4/tv4.js"></script>
  <script src="bower_components/ace-builds/src/ace.js"></script>
  <script src="bower_components/bootstrap/dist/js/bootstrap.js"></script>
  <script src="js/vendor/metaschema.js"></script>
  <script src="js/vendor/metahyperschema.js"></script>
  <script src="js/treema.js"></script>
  <script src="js/base.js"></script>

  <link rel="stylesheet" href="bower_components/jquery-ui/themes/base/jquery-ui.css">
  <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css" media="screen">
  <link rel="stylesheet" href="css/treema.css">
  <link rel="stylesheet" href="css/base.css">
  <link rel="stylesheet" href="css/demo.css">

</head>
<body>

<div id="content">
  <a href="https://github.com/codecombat/treema">
    <img style="position: absolute; top: 0; left: 0; border: 0;"
         src="https://s3.amazonaws.com/github/ribbons/forkme_left_green_007200.png" alt="Fork me on GitHub">
  </a>

  <h1>Treema</h1>

  <ul class="nav nav-pills navbar navbar-default">
    <li><a href="index.html">Overview</a></li>
    <li class="active"><a href="demo.html">Demo</a></li>
    <li><a href="using.html">Usage</a></li>
    <li><a href="develop.html">Dev</a></li>
    <li id="tests-link"><a href="test.html">Run Tests</a></li>
    <li id="github-link"><a href="https://github.com/codecombat/treema">Treema on Github</a></li>
  </ul>

  <p>Let's see this library in action.</p>

  <h3>Basics: A Contact Book</h3>

  <div id="addresses"></div>

  <script>
    var contacts = [
      { 'street-address': '10 Downing Street', 'country-name': 'UK', 'locality': 'London', 'name': 'Prime Minister' },
      { 'street-address': '1600 Amphitheatre Pkwy', 'phone-number': '(650) 253-0000', 'name': 'Google'},
      { 'street-address': '45 Rockefeller Plaza', 'region': 'NY', 'locality': 'New York', 'name': 'Rockefeller Center'},
    ];

    var contact_book_schema = {
      type: 'array',
      items: {
        "additionalProperties": false,
        "type": "object",
        "displayProperty": 'name',
        "properties": {
          "name": { type: "string", maxLength: 20 },
          "street-address": { title: "Address 1", description: "Don't forget the number.", type: "string" },
          "locality":{ "type": "string", title: "Locality" },
          "region": { 'title': 'Region', type: 'string' },
          "country-name": { "type": "string", title: "Country" },
          "friend": { "type": "boolean", title: "Friend" },
          "phone-number": { type: "string", maxLength: 20, minLength:4, title: 'Phone Number' }
        }
      }
    };

    buildTreemaExample($('#addresses'), contact_book_schema, contacts);

  </script>

  <p>
    Try it! See how easy it is to add and remove contacts and properties
    (<strong>Hint</strong>: use keyboard keys like arrows, enter, tab, and escape).
    Check out the schema and data tabs to see what goes in to make this interface
    (and what comes out, the data tab updates based on your changes).
  </p>

  <p>
    Data comes in all shapes and sizes, though, much more than just whether it's a string or a number.
    Treema is built to allow all sorts of ways to set up the interface just the way you like it.
  </p>


  <h2>Custom Nodes</h2>

  <h3>Objects As Single Nodes</h3>

  <p>
    Say your data object has a 2d position in there.
  </p>

  <div id="shapeA"></div>

  <script>
    var shape = {
      origin: {x: 3, y: 4},
      kind: 'circle',
      color: 'red'
    };

    var shape_schema = {
      "additionalProperties": false,
      "type": "object",
      "properties": {
        "origin": {
          "additionalProperties": false,
          type: "object",
          properties: {
            x: { 'type': 'number' },
            y: { 'type': 'number' }
          }
        },
        "kind": {
          type: "string",
          enum: ["square", "circle", "rectangle"]
        },
        color: {
          type: "string",
          enum: ['red', 'blue', 'green']
        }
      }
    };

    treema = buildTreemaExample($('#shapeA'), shape_schema, shape);
    treema.childrenTreemas.origin.open()


  </script>

  <p>
    Even with Treema's compact format, three lines for an object that will only ever have two properties
    just seems like overkill.
    So, define a Treema node that instead treats that object as a single entity, rather than a collection.
  </p>

  <div id="shapeB"></div>

  <script>
    var shape_schema = {
      "additionalProperties": false,
      "type": "object",
      "properties": {
        "origin": {
          type: "object",
          format: "point2d",
          properties: {
            x: { 'type': 'number' },
            y: { 'type': 'number' }
          }
        },
        "kind": {
          type: "string",
          enum: ["square", "circle", "rectangle"]
        },
        color: {
          type: "string",
          enum: ['red', 'blue', 'green']
        }
      }
    };

    treema = buildTreemaExample($('#shapeB'), shape_schema, shape);
  </script>

  <p>
    Much better.
    Use this technique to compress several related properties into single rows,
    like names, addresses and version numbers.
  </p>

  <h3>Embedding Plugins</h3>

  <p>
    <a href="http://ace.c9.io/#nav=about">ACE</a>
    is a great library for creating text editors for all sorts of formats on the web.
    What if some of your nested data is better edited with ACE than a plain textarea or input field?
  </p>

  <div id="component"></div>

  <script>
    var component = {
      name: "Morgan Freeman Plugin",
      description: "Let me tell you something my friend. hope is a dangerous thing. hope can drive a man insane. the man likes to play chess; let's get him some rocks. mister wayne, if you don't want to tell me exactly what you're doing, when i'm asked, i don't have to lie.",
      lang: "JavaScript",
      code: "function Apple (type) {\n  this.type = type;\n  this.color = 'red';\n}\n\nApple.prototype.getInfo = function() {\n  return this.color + ' ' + this.type + ' apple';\n};"
    }

    var component_schema = {
      "additionalProperties": false,
      "type": "object",
      "properties": {
        "name": {
          type: "string",
          maxLength: 40
        },
        "description": {
          type: "string",
          maxLength: 10000
        },
        "lang": {
          type: "string",
          title: "Language",
          enum: ["JavaScript"]
        },
        code: {
          type: "string",
          format: "ace",
          title: "Code",
          aceMode: "ace/mode/javascript"
        }
      }
    };

    treema = buildTreemaExample($('#component'), component_schema, component);
  </script>

  <p>
    With custom nodes, you can weave in whatever elements or specialized plugins you like.
    Have an image URL property use the
    <a href="https://www.inkfilepicker.com/">Ink File Picker</a>.
    Pick date properties with the
    <a href="http://jqueryui.com/datepicker/">JQuery UI Datepicker</a>.
  </p>

  <a id="dbsearch"><h3>References to Other Database Records/Documents</h3></a>

  <p>
    You have users who can list their favorite fast food restaurants.
    This would be a good thing to have separate tables (or collections) for, one for users and one for restaurants.
    But you don't want to have a third table for this minor one-to-many relationship.
    This should all fit just fine into the User entries, along with some basic, denormalized data
    so that when you fetch the User object, you have enough info to show what their favorite restaurants are,
    saving on database queries.
  </p>
  <p>
    The database search Treema node encapsulates all this logic, leaving it to you to
    implement the GET search endpoint, and describe in a function how to format that data.
  </p>

  <div id="lookup"></div>

  <script>
    var lookup = {
      name: "Joe Shmo",
      restaurants: [{"id": 36, "name": "Dairy Queen"}, {"id": 90, "name": "McDonald's"}]
    }

    var lookup_schema = {
      "additionalProperties": false,
      "type": "object",
      "properties": {
        "name": {
          title: "Name",
          type: "string",
          maxLength: 40
        },
        "restaurants": {
          title: "Fast Food Favorites",
          items: { type: "object", format: "restaurant" },
          type: 'array'
        }
      }
    };

    FastFoodSearchTreemaNode = makeFauxSearchTreemaNodeClass('fastfood.json');
    TreemaNode.setNodeSubclass('restaurant', FastFoodSearchTreemaNode);
    treema = buildTreemaExample($('#lookup'), lookup_schema, lookup);
    treema.childrenTreemas.restaurants.open();
  </script>

  <p>
    This is one of the coolest features in Treema.
    Making a simple, consistent pattern for working with denormalized references.
  </p>

  <hr />

  <h3>Implementation</h3>

  <p>
    To see how these and more advanced nodes are implemented, check out
    <a href="https://github.com/codecombat/treema/blob/master/src/extra.coffee" target="_blank">/src/extra.coffee</a>.
  </p>

  <h2>Other Features</h2>

  <h3>Keyboard Shortcuts</h3>

  <p>
    Treema aims to make manipulating these complex data structures as natural and quick as possible.
    To that end, Treema features plenty of keyboard shortcuts so you can stay as far away from your mouse as you like.
    Here's a brief overview:
  </p>

  <ul>
    <li>
      <strong>Arrow keys</strong>: Navigates the selection, opening and closing collections.
    </li><li>
      <strong>Enter and tab</strong>: Adds, edits and navigates between value inputs.
      Use shift to go upward.
    <ul>
      <li><strong>Note</strong>: They have slightly different behaviors.
        Generally speaking, enter is more 'aggressive' with what it does.
        For example, while editing a field whose data is invalid, enter will let you
        move on anyway, and tab will not.
        Enter will open selected collections, tab will not.
      </li>
    </ul>

    </li><li>
      <strong>Escape</strong>: Discards changes on the input currently being edited.
    </li><li>
      <strong>Delete</strong>: Removes a field when the input currently being edited is empty.
    </li>
    <li><strong>Shift and Ctrl-Click</strong>: Selects multiple rows.</li>
  </ul>

  <p>
    The <a href="test.html">tests</a> double as specifications for how shortcuts work, also.
  </p>

  <h3>Structural Restrictions</h3>
  <h4>Schema-Defined, Interface Enforced</h4>

  <p>
    Whenever possible, the interface reflects the limits the schema imposes on the data, including:
  </p>

  <ul>
    <li>
      Preventing adding more elements to arrays and objects when the maximum number
      has been reached, or there are no more properties available to add.
    </li>
    <li>
      Using HTML input types and attributes to enforce rules such as
      length and value minimums and maximums.
    </li>
    <li>
      When there are errors, showing exactly what row or closed collection has issues.
    </li>
  </ul>

  <div id="contactsB"></div>

  <script>
    var contactsB = [
      { 'street-address': '10 Downing Street', 'country-name': 'UK', 'locality': 'London', 'name': 'Prime Minister' },
      { 'phone-number': '(650) 253-0000', 'name': 'Google', 'street-address': '-'},
      { 'street-address': '45 Rockefeller Plaza', 'region': 'NY', 'locality': 'New York', 'name': 'Rockefeller Center'},
    ]

    var contact_book_schemaB = {
      type: 'array',
      items: {
        "additionalProperties": false,
        "type": "object",
        "displayProperty": 'name',
        "properties": {
          "name": { type: "string", maxLength: 20, title: "Name" },
          "street-address": { title: "Address 1", description: "Don't forget the number.", type: "string", minLength: 4 },
          "locality":{ "type": "string", title: "Locality" },
          "region": { 'title': 'Region', type: 'string' },
          "country-name": { "type": "string", title: "Country" },
          "friend": { "type": "boolean", title: "Friend" },
          "phone-number": { type: "string", maxLength: 20, minLength:4, title: 'Phone Number' }
        },
        required: ['street-address', 'name']
      }
    };
    treema = buildTreemaExample($('#contactsB'), contact_book_schemaB, contactsB);
  </script>

  <h3>Callbacks</h3>

  <p>
    You can register callbacks for when:
  </p>

  <ul>
    <li>Rows are hovered onto and off of</li>
    <li>Selection changes</li>
    <li>Data changes</li>
  </ul>

  <p>
    Say the user selects a contact on a contact list.
    Use the callbacks to know when to update another part of the page, showing details about that contact.
  </p>

  <h3>Meta! Create and Edit JSON Schemas</h3>
  <p>
    There's a JSON-Schema for JSON-Schemas of course.
    It uses definitions and self-references, which Treema handles like a charm.
  </p>


  <h4>Core JSON Schema</h4>
  <div id="meta-core"></div>
  <script>
    var schema_schema = {
      "displayProperty": "title",
      "$schema": "http://json-schema.org/draft-04/schema#",
      "title": "Schema",
      "description": "Core schema meta-schema",
      "definitions": {
        "schemaArray": {
          "type": "array",
          "minItems": 1,
          "items": { "$ref": "#" },
          "title": "Array of Schemas",
          "default": [{}]
        },
        "positiveInteger": {
          "type": "integer",
          "minimum": 0,
          "title": "Positive Integer"
        },
        "positiveIntegerDefault0": {
          "allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ]
        },
        "simpleTypes": {
          title: "Single Type",
          "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ]
        },
        "stringArray": {
          "type": "array",
          "items": { "type": "string" },
          "minItems": 1,
          "uniqueItems": true,
          title: "String Array",
          "default": ['']
        }
      },
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "format": "uri"
        },
        "$schema": {
          "type": "string",
          "format": "uri",
          "default": "http://json-schema.org/draft-04/schema#"
        },
        "title": {
          "type": "string"
        },
        "description": {
          "type": "string"
        },
        "default": {},
        "multipleOf": {
          "type": "number",
          "minimum": 0,
          "exclusiveMinimum": true
        },
        "maximum": {
          "type": "number"
        },
        "exclusiveMaximum": {
          "type": "boolean",
          "default": false
        },
        "minimum": {
          "type": "number"
        },
        "exclusiveMinimum": {
          "type": "boolean",
          "default": false
        },
        "maxLength": { "$ref": "#/definitions/positiveInteger" },
        "minLength": { "$ref": "#/definitions/positiveIntegerDefault0" },
        "pattern": {
          "type": "string",
          "format": "regex"
        },
        "additionalItems": {
          "anyOf": [
            { "type": "boolean", "default": false },
            { "$ref": "#" }
          ]
        },
        "items": {
          "anyOf": [
            { "$ref": "#" },
            { "$ref": "#/definitions/schemaArray" }
          ],
          "default": {}
        },
        "maxItems": { "$ref": "#/definitions/positiveInteger" },
        "minItems": { "$ref": "#/definitions/positiveIntegerDefault0" },
        "uniqueItems": {
          "type": "boolean",
          "default": false
        },
        "maxProperties": { "$ref": "#/definitions/positiveInteger" },
        "minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" },
        "required": { "$ref": "#/definitions/stringArray" },
        "additionalProperties": {
          "anyOf": [
            { "type": "boolean", default: true },
            { "$ref": "#" }
          ],
          default: {}
        },
        "definitions": {
          "type": "object",
          "additionalProperties": { "$ref": "#" },
          "default": {}
        },
        "properties": {
          "type": "object",
          "additionalProperties": { "$ref": "#" },
          "default": {}
        },
        "patternProperties": {
          "type": "object",
          "additionalProperties": { "$ref": "#" },
          "default": {}
        },
        "dependencies": {
          "type": "object",
          "additionalProperties": {
            "anyOf": [
              { "$ref": "#" },
              { "$ref": "#/definitions/stringArray" }
            ]
          }
        },
        "enum": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "default": ['']
        },
        "type": {
          "anyOf": [
            { "$ref": "#/definitions/simpleTypes" },
            {
              "type": "array",
              "items": { "$ref": "#/definitions/simpleTypes" },
              "minItems": 1,
              "uniqueItems": true,
              "title": "Array of Types",
              "default": ['string']
            }
          ]
        },
        "allOf": { "$ref": "#/definitions/schemaArray" },
        "anyOf": { "$ref": "#/definitions/schemaArray" },
        "oneOf": { "$ref": "#/definitions/schemaArray" },
        "not": { "$ref": "#" }
      },
      "dependencies": {
        "exclusiveMaximum": [ "maximum" ],
        "exclusiveMinimum": [ "minimum" ]
      },
      "default": {}
    };

    buildTreemaExample($('#meta-core'), schema_schema, contact_book_schemaB.items);
  </script>

  <h3>Want More?</h3>

  <p>
    It's open source!
    <a href="https://github.com/codecombat/treema">Join the project</a> and make Treema even more awesome.
  </p>

  <footer><div></div><div></div></footer>

</div>

</body>
</html>
